﻿/*********************************************************************************************

This class is used with permission from the author and is originally from the 
application ExpressProfiler found in  Codeplex at
http://expressprofiler.codeplex.com

Comments are mine
*********************************************************************************************/

//Traceutils assembly
//writen by Locky, 2009.
using System;
using System.Drawing;
using System.Text;

namespace ExpressProfiler
{
    public class YukonLexer
    {
        public enum TokenKind
        {
            tkComment, tkDatatype,
            tkFunction, tkIdentifier, tkKey, tkNull, tkNumber, tkSpace, tkString, tkSymbol, tkUnknown, tkVariable, tkGreyKeyword, tkFuKeyword
        }

        private enum SqlRange { rsUnknown, rsComment, rsString }
        private readonly Sqltokens m_Tokens = new Sqltokens();

        const string IdentifierStr = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890_#$";
        private readonly char[] m_IdentifiersArray = IdentifierStr.ToCharArray();
        const string HexDigits = "1234567890abcdefABCDEF";
        const string NumberStr = "1234567890.-";
        private int m_StringLen;
        private int m_TokenPos;
        private string m_Token = "";
        private TokenKind m_TokenId;
        private string m_Line;
        private int m_Run;

        private TokenKind TokenId
        {
            get { return this.m_TokenId; }
        }

        private string Token { get { /*int len = this.m_Run - this.m_TokenPos; return this.m_Line.Substring(this.m_TokenPos, len);*/return this.m_Token; } }
        private SqlRange Range { get; set; }

        private char GetChar(int idx)
        {
            return idx >= this.m_Line.Length ? '\x00' : this.m_Line[idx];
        }
        public string StandardSql(string sql)
        {
            StringBuilder result = new StringBuilder();
            this.Line = sql;
            while (this.TokenId != TokenKind.tkNull)
            {
                switch (this.TokenId)
                {
                    case TokenKind.tkNumber:
                    case TokenKind.tkString: result.Append("<??>"); break;
                    default: result.Append(this.Token); break;
                }
                this.Next();
            }
            return result.ToString();
        }
        public YukonLexer() { Array.Sort(this.m_IdentifiersArray); }

        public void FillRichEdit(System. Windows.Forms.RichTextBox rich, string value)
        {
            rich.Text = "";
			string rtf = this.CreateRtfString(value, rich.BackColor);
            rich.Rtf = rtf;
        }

		public string CreateRtfString(string value, Color richBackColor)
		{
			this.Line = value;

			RTFBuilder sb = new RTFBuilder { BackColor = richBackColor };
			sb.BackColor = Color.FromArgb(255, 255, 24);
			sb.BackColor = richBackColor;

			while (this.TokenId != TokenKind.tkNull)
			{
				Color forecolor;
				switch (this.TokenId)
				{
					case TokenKind.tkKey: forecolor = Color.Blue;
						break;
					case TokenKind.tkFunction: forecolor = Color.Fuchsia; break;
					case TokenKind.tkGreyKeyword: forecolor = Color.Gray; break;
					case TokenKind.tkFuKeyword: forecolor = Color.Fuchsia; break;
					case TokenKind.tkDatatype: forecolor = Color.Blue; break;
					case TokenKind.tkNumber: forecolor = Color.Red; break;
					case TokenKind.tkString: forecolor = Color.Red; break;
					case TokenKind.tkComment: forecolor = Color.DarkGreen;
						break;
					default: forecolor = Color.Black; break;
				}
				sb.ForeColor = forecolor;
				if (this.Token == Environment.NewLine || this.Token == "\r" || this.Token == "\n")
				{
					sb.AppendLine();
				}
				else
				{
					sb.Append(this.Token);
				}
				this.Next();
			}
			return  sb.ToString();
		}

        private string Line
        {
            set { this.Range = SqlRange.rsUnknown; this.m_Line = value; this.m_Run = 0; this.Next(); }
        }
        private void NullProc() { this.m_TokenId = TokenKind.tkNull; }
        // ReSharper disable InconsistentNaming
        private void LFProc() { this.m_TokenId = TokenKind.tkSpace; this.m_Run++; }
        private void CRProc() { this.m_TokenId = TokenKind.tkSpace; this.m_Run++; if (GetChar(this.m_Run) == '\x0A')this.m_Run++; }
        // ReSharper restore InconsistentNaming

        private void AnsiCProc()
        {
            switch (this.GetChar(this.m_Run))
            {
                case '\x00': this.NullProc(); break;
                case '\x0A': this.LFProc(); break;
                case '\x0D': this.CRProc(); break;

                default:
                    {
                        this.m_TokenId = TokenKind.tkComment;
                        char c;
                        do
                        {
                            if (GetChar(this.m_Run) == '*' && this.GetChar(this.m_Run + 1) == '/')
                            {
                                this.Range = SqlRange.rsUnknown; this.m_Run += 2; break;
                            }
                            this.m_Run++;
                            c = this.GetChar(this.m_Run);
                        }
                        while (!(c == '\x00' || c == '\x0A' || c == '\x0D'));
                        break;
                    }



            }
        }

        private void AsciiCharProc()
        {
            if (this.GetChar(this.m_Run) == '\x00') { this.NullProc(); }
            else
            {
                this.m_TokenId = TokenKind.tkString;
                if (this.m_Run > 0 || Range != SqlRange.rsString || GetChar(this.m_Run) != '\x27')
                {
                    this.Range = SqlRange.rsString;
                    char c;
                    do { this.m_Run++; c = this.GetChar(this.m_Run); } while (!(c == '\x00' || c == '\x0A' || c == '\x0D' || c == '\x27'));
                    if (GetChar(this.m_Run) == '\x27')
                    {
                        this.m_Run++;
                        Range = SqlRange.rsUnknown;
                    }
                }
            }
        }

        private void DoProcTable(char chr)
        {
            switch (chr)
            {
                case '\x00': NullProc(); break;
                case '\x0A': LFProc(); break;
                case '\x0D': CRProc(); break;
                case '\x27': AsciiCharProc(); break;

                case '=': EqualProc(); break;
                case '>': GreaterProc(); break;
                case '<': LowerProc(); break;
                case '-': MinusProc(); break;
                case '|': OrSymbolProc(); break;
                case '+': PlusProc(); break;
                case '/': SlashProc(); break;
                case '&': AndSymbolProc(); break;
                case '\x22': QuoteProc(); break;
                case ':':
                case '@': VariableProc(); break;
                case '^':
                case '%':
                case '*':
                case '!': SymbolAssignProc(); break;
                case '{':
                case '}':
                case '.':
                case ',':
                case ';':
                case '?':
                case '(':
                case ')':
                case ']':
                case '~': SymbolProc(); break;
                case '[': BracketProc(); break;
                default:
                    DoInsideProc(chr); break;

            }
        }

        private void DoInsideProc(char chr)
        {

            if ((chr >= 'A' && chr <= 'Z') || (chr >= 'a' && chr <= 'z') || (chr == '_') || (chr == '#')) { IdentProc(); return; }
            if (chr >= '0' && chr <= '9') { NumberProc(); return; }
            if ((chr >= '\x00' && chr <= '\x09') || (chr >= '\x0B' && chr <= '\x0C') || (chr >= '\x0E' && chr <= '\x20')) { SpaceProc(); return; }
            UnknownProc();
        }

        private void SpaceProc()
        {
            this.m_TokenId = TokenKind.tkSpace;
            char c;
            do { this.m_Run++; c = GetChar(this.m_Run); }
            while (!(c > '\x20' || c == '\x00' || c == '\x0A' || c == '\x0D'));
        }

        private void UnknownProc()
        {
            this.m_Run++;
            this.m_TokenId = TokenKind.tkUnknown;
        }

        private void NumberProc()
        {
            this.m_TokenId = TokenKind.tkNumber;
            if (GetChar(this.m_Run) == '0' && (GetChar(this.m_Run) == 'X' || GetChar(this.m_Run) == 'x'))
            {
                this.m_Run += 2;
                while (HexDigits.IndexOf(GetChar(this.m_Run)) != -1) this.m_Run++;
                return;
            }
            this.m_Run++;
            this.m_TokenId = TokenKind.tkNumber;
            while (NumberStr.IndexOf(GetChar(this.m_Run)) != -1)
            {
                if (GetChar(this.m_Run) == '.' && GetChar(this.m_Run + 1) == '.') break;
                this.m_Run++;
            }

        }

        private void QuoteProc()
        {
            this.m_TokenId = TokenKind.tkIdentifier;
            this.m_Run++;
            while (!(GetChar(this.m_Run) == '\x00' || GetChar(this.m_Run) == '\x0A' || GetChar(this.m_Run) == '\x0D'))
            {
                if (GetChar(this.m_Run) == '\x22') { this.m_Run++; break; }
                this.m_Run++;
            }
        }

        private void BracketProc()
        {

            this.m_TokenId = TokenKind.tkIdentifier;
            this.m_Run++;
            while (!(GetChar(this.m_Run) == '\x00' || GetChar(this.m_Run) == '\x0A' || GetChar(this.m_Run) == '\x0D'))
            {
                if (GetChar(this.m_Run) == ']') { this.m_Run++; break; }
                this.m_Run++;
            }

        }

        private void SymbolProc()
        {
            this.m_Run++;
            this.m_TokenId = TokenKind.tkSymbol;
        }

        private void SymbolAssignProc()
        {
            this.m_TokenId = TokenKind.tkSymbol;
            this.m_Run++;
            if (GetChar(this.m_Run) == '=') this.m_Run++;
        }

        private void KeyHash(int pos)
        {
            this.m_StringLen = 0;
            while (Array.BinarySearch(this.m_IdentifiersArray, GetChar(pos)) >= 0) { this.m_StringLen++; pos++; }
            return;
        }
        private TokenKind IdentKind()
        {
            KeyHash(this.m_Run);
            return this.m_Tokens[this.m_Line.Substring(this.m_TokenPos, this.m_Run + this.m_StringLen - this.m_TokenPos)];
        }
        private void IdentProc()
        {
            this.m_TokenId = IdentKind();
            this.m_Run += this.m_StringLen;
            if (this.m_TokenId == TokenKind.tkComment)
            {
                while (!(GetChar(this.m_Run) == '\x00' || GetChar(this.m_Run) == '\x0A' || GetChar(this.m_Run) == '\x0D')) { this.m_Run++; }
            }
            else
            {
                while (IdentifierStr.IndexOf(GetChar(this.m_Run)) != -1) this.m_Run++;
            }
        }
        private void VariableProc()
        {
            if (GetChar(this.m_Run) == '@' && GetChar(this.m_Run + 1) == '@') { this.m_Run += 2; IdentProc(); }
            else
            {
                this.m_TokenId = TokenKind.tkVariable;
                int i = this.m_Run;
                do { i++; } while (!(IdentifierStr.IndexOf(GetChar(i)) == -1));
                this.m_Run = i;
            }
        }

        private void AndSymbolProc()
        {
            this.m_TokenId = TokenKind.tkSymbol;
            this.m_Run++;
            if (GetChar(this.m_Run) == '=' || GetChar(this.m_Run) == '&') this.m_Run++;
        }

        private void SlashProc()
        {
            this.m_Run++;
            switch (GetChar(this.m_Run))
            {
                case '*':
                    {
                        Range = SqlRange.rsComment;
                        this.m_TokenId = TokenKind.tkComment;
                        do
                        {
                            this.m_Run++;
                            if (GetChar(this.m_Run) == '*' && GetChar(this.m_Run + 1) == '/') { Range = SqlRange.rsUnknown; this.m_Run += 2; break; }
                        } while (!(GetChar(this.m_Run) == '\x00' || GetChar(this.m_Run) == '\x0D' || GetChar(this.m_Run) == '\x0A'));
                    }
                    break;
                case '=': this.m_Run++; this.m_TokenId = TokenKind.tkSymbol; break;
                default: this.m_TokenId = TokenKind.tkSymbol; break;

            }
        }

        private void PlusProc()
        {
            this.m_TokenId = TokenKind.tkSymbol;
            this.m_Run++;
            if (GetChar(this.m_Run) == '=' || GetChar(this.m_Run) == '=') this.m_Run++;

        }

        private void OrSymbolProc()
        {
            this.m_TokenId = TokenKind.tkSymbol;
            this.m_Run++;
            if (GetChar(this.m_Run) == '=' || GetChar(this.m_Run) == '|') this.m_Run++;
        }

        private void MinusProc()
        {
            this.m_Run++;
            if (GetChar(this.m_Run) == '-')
            {
                this.m_TokenId = TokenKind.tkComment;
                char c;
                do
                {
                    this.m_Run++;
                    c = GetChar(this.m_Run);
                } while (!(c == '\x00' || c == '\x0A' || c == '\x0D'));
            }
            else { this.m_TokenId = TokenKind.tkSymbol; }
        }

        private void LowerProc()
        {
            this.m_TokenId = TokenKind.tkSymbol;
            this.m_Run++;
            switch (GetChar(this.m_Run))
            {
                case '=': this.m_Run++; break;
                case '<': this.m_Run++; if (GetChar(this.m_Run) == '=') this.m_Run++; break;
            }

        }

        private void GreaterProc()
        {
            this.m_TokenId = TokenKind.tkSymbol;
            this.m_Run++;
            if (GetChar(this.m_Run) == '=' || GetChar(this.m_Run) == '>') this.m_Run++;
        }

        private void EqualProc()
        {
            this.m_TokenId = TokenKind.tkSymbol;
            this.m_Run++;
            if (GetChar(this.m_Run) == '=' || GetChar(this.m_Run) == '>') this.m_Run++;
        }

        private void Next()
        {
            this.m_TokenPos = this.m_Run;
            switch (Range)
            {
                case SqlRange.rsComment: AnsiCProc(); break;
                case SqlRange.rsString: AsciiCharProc(); break;
                default: DoProcTable(GetChar(this.m_Run)); break;
            }
            this.m_Token = this.m_Line.Substring(this.m_TokenPos, this.m_Run - this.m_TokenPos);
        }

    }
}